package org.xx.armory.bindings;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.StringUtils.trimToEmpty;
import static org.xx.armory.commons.SysUtils.stringEquals;
import static org.xx.armory.commons.Validators.notNull;

/**
 * 包含绑定参数的集合。
 * <p>
 * 该集合内部使用{@code java.util.List}保存绑定参数，所以确保所有的元素都按照加入的顺序排列。
 * </p>
 *
 * @author Haart
 */
public final class BoundParamCollection
        implements Collection<BoundParam> {
    private final List<BoundParam> params;

    /**
     * 构造绑定参数集合。
     */
    public BoundParamCollection() {
        this.params = new ArrayList<>();
    }

    /**
     * 复制构造绑定参数集合。
     *
     * @param other
     *         被复制的集合。
     * @throws IllegalArgumentException
     *         如果参数{@code other}是 {@code null}。
     */
    public BoundParamCollection(
            BoundParamCollection other
    ) {
        notNull(other, "other");

        this.params = new ArrayList<>(other.params);
    }

    /**
     * 判断是否包含指定名字的参数。
     *
     * @param name
     *         参数的名字，会自动去掉首尾空格。
     * @return 如果包含则返回{@code true}，否则返回{@code false}。
     */
    public final boolean containsName(
            String name
    ) {
        name = trimToEmpty(name);

        for (final BoundParam param : this.params) {
            if (stringEquals(param.getName(), name)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 根据名字获取参数。
     *
     * @param name
     *         参数的名字，会自动去掉首尾空格。
     * @return 如果找到了参数则返回值，否则返回{@code null}，如果参数名只包含空白字符也返回{@code null}。
     */
    public final Object get(
            String name
    ) {
        name = trimToEmpty(name);

        if (name.isEmpty()) {
            return null;
        }

        for (final BoundParam param : this.params) {
            if (stringEquals(param.getName(), name)) {
                return param.getValue();
            }
        }
        return null;
    }

    /**
     * 获取包含所有参数名的集合。
     *
     * @return 包含所有参数名的集合。
     */
    public final Collection<String> names() {
        return this.params.stream()
                          .map(BoundParam::getName)
                          .collect(Collectors.toList());
    }

    /**
     * 获取包含所有参数值的集合。
     *
     * @return 包含所有参数值的集合。
     */
    public final Collection<Object> values() {
        return this.params.stream()
                          .map(BoundParam::getValue)
                          .collect(Collectors.toList());
    }

    /**
     * 设置参数值。
     *
     * @param name
     *         参数的名字，会自动去掉首尾空格。
     * @param value
     *         参数的值。
     */
    public final void put(
            String name,
            Object value
    ) {
        name = trimToEmpty(name);
        this.params.add(new BoundParam(name, value));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final int size() {
        return this.params.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean isEmpty() {
        return this.params.isEmpty();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean contains(
            Object o
    ) {
        return this.params.contains(o);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Iterator<BoundParam> iterator() {
        return this.params.iterator();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final Object[] toArray() {
        return this.params.toArray();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings("SuspiciousToArrayCall")
    public <T> T[] toArray(
            T[] a
    ) {
        return this.params.toArray(a);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean add(
            BoundParam newParam
    ) {
        if (newParam != null) {
            for (final BoundParam param : this.params) {
                if (stringEquals(param.getName(), newParam.getName())) {
                    return false;
                }
            }

            return this.params.add(newParam);
        } else {
            return true;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean remove(
            Object o
    ) {
        return this.params.remove(o);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean containsAll(
            Collection<?> c
    ) {
        return this.params.containsAll(c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean addAll(
            Collection<? extends BoundParam> c
    ) {
        // TODO Auto-generated method stub
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean removeAll(
            Collection<?> c
    ) {
        return this.params.removeAll(c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean retainAll(
            Collection<?> c
    ) {
        return this.params.retainAll(c);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void clear() {
        this.params.clear();
    }
}
